/*
* Copyright (c) 2008, Vladimir Gumenuk
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
*     * Redistributions of source code must retain the above copyright
*       notice, this list of conditions and the following disclaimer.
*     * Redistributions in binary form must reproduce the above copyright
*       notice, this list of conditions and the following disclaimer in the
*       documentation and/or other materials provided with the distribution.
*     * Neither the name of the copyright holder nor the
*       names of its contributors may be used to endorse or promote products
*       derived from this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDER "AS IS" AND ANY
* EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
* DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER BE LIABLE FOR ANY
* DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/

#include "StdAfx.h"
#include "xmlutils.h"
#include "GP4File.h"
#include "FileSearch.h"

CGP4File::CGP4File()
{
}

CGP4File::~CGP4File()
{

}

void CGP4File::TraceParams()
{
    MAP_PARAMS::iterator theIter = m_mapParams.begin();
    for( ; theIter != m_mapParams.end(); theIter++ )
    {
        TRACE( "ID: %04d --------------\n", theIter->first );

        VEC_PARAMS& vecParams = theIter->second;
        
        for( DWORD i = 0; i < vecParams.size(); i++ )
        {
            CGP4Param& par = vecParams[i];
            TRACE( "\tID: %04d, Name: '%s', Value: %d, Pos: %d, Text: %s\n", par.m_dwID, par.m_csName, par.m_dwValue, par.m_dwPos, par.m_csText );
        }
    }
}

void CGP4File::LoadAllParams( LPCSTR lpszFromFolder, CGP4File& file, LPPRESET pArray )
{
    TCHAR szFromDir[MAX_PATH];
    CFileSearch Search( lpszFromFolder, _T("*.*") );

    while( Search.FindNextFile() )
    {
        if( Search.IsFolder() ) // recursively parse this folder
        {
            _tcscpy( szFromDir, lpszFromFolder );
            _tcscat( szFromDir, _T("\\") );
            _tcscat( szFromDir, Search.GetFileName() );

            LoadAllParams( szFromDir, file, pArray );
        }
        else
        {
            LPCTSTR lpstrDot = _tcsrchr( Search.GetFileName(), '.' );
            if( lpstrDot )
            {
                if( 0 == _tcsicmp( lpstrDot, _T(".g4p") ) )
                {
                    PRESET p;
                    ZeroMemory( &p, sizeof(PRESET) );
                    if( file.Load( Search.GetFullPath(), &p ) )
                    {
                    }
                }
            }
        }
    }
}

BOOL CGP4File::Load( LPCSTR lpszFilePath, LPPRESET p )
{
    ASSERT( lpszFilePath );
    ASSERT( p );

    BOOL bRet = FALSE;
    CString csError;
    HRESULT hr = E_FAIL;
    MSXML::IXMLDOMDocumentPtr spDoc;

    // SetPresetDefaults( p ); - NOT yet, it is still better to take std preset and change only known params
    
    if( LoadXMLDocument( lpszFilePath, TRUE, spDoc, csError ) )
    {
        MSXML::IXMLDOMNodeListPtr spNodeList;
        if( SUCCEEDED( hr = spDoc->get_childNodes( &spNodeList ) ) )
        {
            long lCount;
            if( SUCCEEDED( hr = spNodeList->get_length( &lCount ) ) )
            {
                for( long i = 0; i < lCount; i++ )
                {
                    MSXML::IXMLDOMNodePtr spNode;
                    if( SUCCEEDED( hr = spNodeList->get_item( i, &spNode ) ) )
                    {
                        CComBSTR bsName;
                        if( SUCCEEDED( hr = spNode->get_nodeName( &bsName ) ) )
                        {
                            if( bsName == L"Gnx4Preset" )
                            {
                                hr = ProcessGnx4PresetNode( spNode, p );
                                break;
                            }
                        }
                    }
                }
            }
        }

        if( FAILED( hr ) )
        {
            CString csFmt;
            csFmt.Format( "Load gp4 file failed!: '%s'\n", lpszFilePath );
            AfxMessageBox( csFmt );
            TRACE( "Load gp4 file failed!: '%s'\n", lpszFilePath );
        }
        else
            bRet = TRUE;
    } else
        AfxMessageBox( csError );

    return bRet;
}

HRESULT CGP4File::ProcessGnx4PresetNode( MSXML::IXMLDOMNodePtr& spPresetNode, LPPRESET p )
{
    HRESULT hr;
    MSXML::IXMLDOMNodePtr spNameNode, spParamsNode, spGenetxNode;

    if( FAILED( hr = GetNode( spPresetNode, _bstr_t( L"Name" ), spNameNode ) ) )
        return hr;

    if( FAILED( hr = GetNode( spPresetNode, _bstr_t( L"Params" ), spParamsNode ) ) )
        return hr;

    if( FAILED( hr = GetNode( spPresetNode, _bstr_t( L"Genetx" ), spGenetxNode ) ) )
        return hr;

    CComBSTR bsName;
    if( FAILED( hr = spNameNode->get_text( &bsName ) ) )
        return hr;
    StoreGNXStringW( &p->wszName[0], bsName );

    if( FAILED( hr = ProcessParamsNode( spParamsNode, p ) ) )
        return hr;

    if( FAILED( hr = ProcessGenetxNode( spGenetxNode, p ) ) )
    {
        ASSERT(0);
    }

    return hr;
}

HRESULT CGP4File::ProcessParamsNode( MSXML::IXMLDOMNodePtr& spParamsNode, LPPRESET p )
{
    HRESULT hr = S_OK;
    MSXML::IXMLDOMNodeListPtr spNodeList;
    
    if( SUCCEEDED( hr = spParamsNode->get_childNodes( &spNodeList ) ) )
    {
        long lCount;
        if( SUCCEEDED( hr = spNodeList->get_length( &lCount ) ) )
        {
            //TRACE( "Params count: %d\n", lCount );
            for( long i = 0; i < lCount; i++ )
            {
                MSXML::IXMLDOMNodePtr spParam;
                if( SUCCEEDED( hr = spNodeList->get_item( i, &spParam ) ) )
                {
                    CGP4Param par;
                    if( SUCCEEDED( hr = GetParamValues( spParam, par ) ) )
                    {
                        CopyParamValueToPreset( par, p );
                    } else
                        break;

                }
            }
        }
    }

    return hr;
}

HRESULT CGP4File::GetParamValues( MSXML::IXMLDOMNodePtr& spParam, CGP4Param& par )
{
    HRESULT hr = S_OK;
    // ID Position Value Name Text
    CString csID, csPos, csVal;

    if( FAILED( hr = GetNodeText( spParam, _bstr_t( L"ID" ), csID ) ) )
        return hr;

    if( FAILED( hr = GetNodeText( spParam, _bstr_t( L"Position" ), csPos ) ) )
        return hr;

    if( FAILED( hr = GetNodeText( spParam, _bstr_t( L"Value" ), csVal ) ) )
        return hr;

    if( FAILED( hr = GetNodeText( spParam, _bstr_t( L"Name" ), par.m_csName ) ) )
        return hr;

    if( FAILED( hr = GetNodeText( spParam, _bstr_t( L"Text" ), par.m_csText ) ) )
        return hr;

    par.m_dwID    = (DWORD)atol( csID );
    par.m_dwPos   = (DWORD)atol( csPos );
    par.m_dwValue = (DWORD)atol( csVal );

    //TRACE( "ID: %04d, Name: %s, Value: %d, Pos: %d, Text: %s\n", par.m_dwID, par.m_csName, par.m_dwValue, par.m_dwPos, par.m_csText );
    //CString csTemp = par.csName;
    //csTemp.Replace( ' ', '_' );
    //csTemp.Replace( '-', '_' );
    //TRACE( "case GP4_%s       %d    // 0x%08X\n", csTemp, par.dwID, par.dwID );

    /*
    MAP_PARAMS::iterator theIter = m_mapParams.find( par.m_dwID );
    if( theIter != m_mapParams.end() )
    {
        theIter->second.push_back( par );
    }
    else
    {
        VEC_PARAMS vec;
        vec.push_back( par );
        m_mapParams.insert( MAP_PARAMS::value_type( par.m_dwID, vec ) );
    }*/

    return hr;
}

HRESULT CGP4File::ProcessGenetxNode( MSXML::IXMLDOMNodePtr& spGenetxNode, LPPRESET p )
{
    HRESULT hr;
    MSXML::IXMLDOMNodeListPtr spNodeList;

    if( SUCCEEDED( hr = spGenetxNode->get_childNodes( &spNodeList ) ) )
    {
        long lCount;
        if( SUCCEEDED( hr = spNodeList->get_length( &lCount ) ) )
        {
            for( long i = 0; i < lCount; i++ )
            {
                MSXML::IXMLDOMNodePtr spGenetx;
                if( SUCCEEDED( hr = spNodeList->get_item( i, &spGenetx ) ) )
                {
                    if( FAILED( hr = ProcessGenetxModel( spGenetx, p ) ) )
                        break;

                }
            }
        }
    }

    return hr;
}

HRESULT CGP4File::ProcessGenetxModel( MSXML::IXMLDOMNodePtr& spGenetxModel, LPPRESET p )
{
    HRESULT hr = S_OK;
    // Version Type Channel Name Data
    CString csVersion, csType, csChannel, csName, csData;

    if( FAILED( hr = GetNodeText( spGenetxModel, _bstr_t( L"Version" ), csVersion ) ) )
    {
        TRACE( "WARNING: No Version!\n" );
    }

    if( FAILED( hr = GetNodeText( spGenetxModel, _bstr_t( L"Type" ), csType ) ) )
        return hr;

    if( FAILED( hr = GetNodeText( spGenetxModel, _bstr_t( L"Channel" ), csChannel ) ) )
        return hr;

    if( FAILED( hr = GetNodeText( spGenetxModel, _bstr_t( L"Name" ), csName ) ) )
        return hr;

    if( FAILED( hr = GetNodeText( spGenetxModel, _bstr_t( L"Data" ), csData ) ) )
        return hr;

    int iDstLen;
    LPBYTE pDecodeBuf = NULL;
    USES_CONVERSION;

    LPBYTE lpTemp = new BYTE[1024]; // should be enough
    if( !lpTemp )
        return E_OUTOFMEMORY;

    ZeroMemory( lpTemp, 1024 );

    DWORD dwChannel = 0;
    DWORD dwType = 0;
    GNX_AMP *pAmp = NULL;

    if( csChannel == "Channel1" )
    {
        dwChannel = 1;

        if( csType == "Amp" )
        {
            pAmp       = &p->Amp1;
            iDstLen    = GNX_AMP_DATASIZE;
            StoreGNXStringW( &p->Amp1.wszName[0], A2W(csName) );
            dwType = 1;
        }
        else // cab
        {
            pDecodeBuf = (LPBYTE)&p->Cab1Data[0];
            iDstLen    = GNX_CAB_DATASIZE;
            StoreGNXStringW( &p->wszCab1Name[0], A2W(csName) );
            dwType = 2;
        }   

    }
    else // 2
    { 
        dwChannel = 2;
        if( csType == "Amp" )
        {
            pAmp       = &p->Amp2;
            iDstLen    = GNX_AMP_DATASIZE;
            StoreGNXStringW( &p->Amp2.wszName[0], A2W(csName) );
            dwType = 1;
        }
        else // cab
        {
            pDecodeBuf = (LPBYTE)&p->Cab2Data[0];
            iDstLen    = GNX_CAB_DATASIZE;
            StoreGNXStringW( &p->wszCab2Name[0], A2W(csName) );
            dwType = 2;
        }
    }

    iDstLen = 1024;

    if( Base64Decode( csData, csData.GetLength(), lpTemp, &iDstLen ) )
    {
        //TRACE( "Base64Decode - len: %d\n", iDstLen );

        if( dwType == 1 )
        {
            ASSERT( pAmp );
            //PrintHexDump( lpTemp, (DWORD)iDstLen );
            DecompressAmpData( (LPBYTE)pAmp, lpTemp, iDstLen );
            //DecompressAmpData2( pAmp, lpTemp, (DWORD)iDstLen );
        }
        else
        {
            ASSERT( pDecodeBuf );
            memcpy( pDecodeBuf, lpTemp, iDstLen );
        }
    }
    else
    {
        ASSERT(0);
        hr = E_FAIL;
    }

    delete []lpTemp;

    return hr;
}

// this algo developed blindly
void CGP4File::DecompressAmpData( LPBYTE pDst, LPBYTE lpSrc, DWORD dwSrcLen )
{
    DWORD Idx = 0, x = 0;

    for( int i = 0; i < 188; i++ )
    {
        BYTE b1 = lpSrc[x++];
        BYTE b2 = lpSrc[x++];
        BYTE b3 = lpSrc[x++];

        pDst[Idx++] = b3;
        pDst[Idx++] = 0;
        pDst[Idx++] = b1;
        pDst[Idx++] = b2;
        //pAmp->dwSplineCoefData[i] = (DWORD)( ( b2 << 24 | b1 << 16 | b3 ) & 0xFFFF00FF );
    }

    /*for( int i = 0; i < 8; i++ ) // decompress 1st block - 8 lines x12 bytes
    {
        pDst[Idx++] = 0;
        pDst[Idx++] = 0;
        pDst[Idx++] = lpSrc[x++];
        pDst[Idx++] = lpSrc[x++];

        pDst[Idx++] = lpSrc[x++];
        pDst[Idx++] = 0;
        pDst[Idx++] = lpSrc[x++];
        pDst[Idx++] = lpSrc[x++];

        pDst[Idx++] = lpSrc[x++];
        pDst[Idx++] = 0;
        pDst[Idx++] = lpSrc[x++];
        pDst[Idx++] = lpSrc[x++];

        pDst[Idx++] = lpSrc[x+4]; // insert here last byte from 12-byte sequence
        pDst[Idx++] = lpSrc[x++];
        pDst[Idx++] = lpSrc[x++];
        pDst[Idx++] = lpSrc[x++]; x++; // and skip last byte, we have inserted it already
    }

    for( int i = 0; i < 8; i++ ) // decompress 2nd 8x12 block
    {
        pDst[Idx++] = lpSrc[x+2];
        pDst[Idx++] = 0;
        pDst[Idx++] = lpSrc[x++];
        pDst[Idx++] = lpSrc[x++]; x++;

        pDst[Idx++] = 0;
        pDst[Idx++] = 0;
        pDst[Idx++] = lpSrc[x++];
        pDst[Idx++] = lpSrc[x++];

        pDst[Idx++] = lpSrc[x+3];
        pDst[Idx++] = lpSrc[x++];
        pDst[Idx++] = lpSrc[x++];
        pDst[Idx++] = lpSrc[x++]; x++;

        pDst[Idx++] = lpSrc[x+2];
        pDst[Idx++] = 0;
        pDst[Idx++] = lpSrc[x++];
        pDst[Idx++] = lpSrc[x++]; x++;
    }

    for( int i = 0; i < 31; i++ ) // decompress 3, 4, 5, and half 6 8x12 block
    {
        pDst[Idx++] = lpSrc[x+2];
        pDst[Idx++] = 0;
        pDst[Idx++] = lpSrc[x++];
        pDst[Idx++] = lpSrc[x++]; x++;

        pDst[Idx++] = lpSrc[x+2];
        pDst[Idx++] = 0;
        pDst[Idx++] = lpSrc[x++];
        pDst[Idx++] = lpSrc[x++]; x++;

        pDst[Idx++] = lpSrc[x+2];
        pDst[Idx++] = 0;
        pDst[Idx++] = lpSrc[x++];
        pDst[Idx++] = lpSrc[x++]; x++;

        pDst[Idx++] = lpSrc[x+2];
        pDst[Idx++] = 0;
        pDst[Idx++] = lpSrc[x++];
        pDst[Idx++] = lpSrc[x++]; x++;
    }*/

    DWORD tmp = x;
    // The next 6 bytes is nightmare. The algo that Ive found just produces the similar results on the std presets.
    pDst[Idx++] = lpSrc[x+1] - 0x32;
    pDst[Idx++] = lpSrc[x++]; x++;
    pDst[Idx++] = lpSrc[x+1] + 0xD4;
    pDst[Idx++] = lpSrc[x++] - 0x02; x++; // not always true :(

    BYTE b1 = lpSrc[x++];
    BYTE b2 = lpSrc[x++];

    pDst[Idx++] = b1 + b2;    // not always true :(
    pDst[Idx++] = 0x0A;       // not always true :(
    pDst[Idx++] = lpSrc[x++]; // the rest is ok...
    pDst[Idx++] = 0;

    pDst[Idx++] = lpSrc[x++];
    pDst[Idx++] = 0;
    pDst[Idx++] = lpSrc[x++];
    pDst[Idx++] = 0;

    pDst[Idx++] = lpSrc[tmp+23];
    pDst[Idx++] = 0;
    pDst[Idx++] = lpSrc[x++];
    pDst[Idx++] = 0;

    pDst[Idx++] = lpSrc[x++];
    pDst[Idx++] = 0;
    pDst[Idx++] = 0xFF;
    pDst[Idx++] = 0xFF;

    pDst[Idx++] = lpSrc[x+2];
    pDst[Idx++] = 0;
    pDst[Idx++] = lpSrc[x++];
    pDst[Idx++] = lpSrc[x++]; x++;

    pDst[Idx++] = lpSrc[x+2];
    pDst[Idx++] = 0;
    pDst[Idx++] = lpSrc[x++];
    pDst[Idx++] = lpSrc[x++]; x++;

    pDst[Idx++] = lpSrc[x+2];
    pDst[Idx++] = 0;
    pDst[Idx++] = lpSrc[x++];
    pDst[Idx++] = lpSrc[x++]; x++;

    pDst[Idx++] = lpSrc[x+2];
    pDst[Idx++] = 0;
    pDst[Idx++] = lpSrc[x++];
    pDst[Idx++] = lpSrc[x++]; x++;

    TRACE( "CGP4File::DecompressAmpData: desompressed size = 0x%04X - %d, src used: %d\n", Idx, Idx, x );
}

// second attempt, I hope will be successful this time :)
void CGP4File::DecompressAmpData2( GNX_AMP *pAmp, LPBYTE pSrc, DWORD dwSrcLen )
{
    // decompress spline coefficients - 3 bytes per 32 bit value
    for( DWORD i = 0; i < NUM_SPLINE_COEFS; i++ )
    {
        BYTE b1 = *pSrc++;
        BYTE b2 = *pSrc++;
        BYTE b3 = *pSrc++;
        pAmp->dwSplineCoefData[i] = (DWORD)( ( b2 << 24 | b1 << 16 | b3 ) & 0xFFFF00FF );
    }

    // decompress Eq ZRegs
    for( DWORD i = 0; i < NUM_MODEL_EQ_ZREGS; i++ )
    {
        BYTE b1 = *pSrc++;
        BYTE b2 = *pSrc++;
        BYTE b3 = *pSrc++;
        pAmp->dwModelEqZRegData[i] = (DWORD)( ( b2 << 24 | b1 << 16 | b3 ) & 0xFFFF00FF );
    }

    // THIS is absolutely invalid!!! :(
    pAmp->def_Tone.wBass_Freq   = *pSrc++;
    pAmp->def_Tone.wMid_Freq    = *pSrc++;
    pAmp->def_Tone.wHi_Freq     = *pSrc++;
    pAmp->def_Tone.wBass_Level  = *pSrc++;
    pAmp->def_Tone.wMid_Level   = *pSrc++;
    pAmp->def_Tone.wHi_Level    = *pSrc++;
    pAmp->def_Tone.wPresence    = *pSrc++;
    pAmp->def_Tone.wAmp_Gain    = *pSrc++;
    pAmp->def_Tone.wAmp_Level   = *pSrc++;

    pAmp->def_Range.dwGain_Max  = *pSrc++;
    pAmp->def_Range.dwGain_Min  = *pSrc++;
    pAmp->def_Range.dwLevel_Max = *pSrc++;
    pAmp->def_Range.dwLevel_Min = *pSrc++;
}

void CGP4File::CopyParamValueToPreset( const CGP4Param& par, LPPRESET p )
{
    WORD wValue = (WORD)par.m_dwValue;

    switch( par.m_dwID )
    {
    case GP4_Pickup_Type:
        if( par.m_dwValue == 65 /*SC->Hum*/ )      p->wPICKUP_Type = 0;
        else if( par.m_dwValue == 66 /*Hum->SC*/ ) p->wPICKUP_Type = 1;
        else ASSERT(0);
        break;
    case GP4_Pickup_Enable:          p->wPICKUP_OnOff           = wValue;    break;
    case GP4_Wah_Type:
        if( par.m_dwValue == 129 /*Cry*/ )             p->wWAH_Type = 0;
        else if( par.m_dwValue == 130 /*Boutique*/ )   p->wWAH_Type = 1;
        else if( par.m_dwValue == 131 /*Full-range*/ ) p->wWAH_Type = 2;
        else ASSERT(0);
        break;
    case GP4_Wah_Enable:             p->wWAH_OnOff              = wValue;    break;
    case GP4_Wah_Position:           p->wWAH_Pedal              = wValue;    break;
    case GP4_Compressor_Enable:      p->wCOMPRESSOR_OnOff       = wValue;    break;
    case GP4_Compressor_Attack:      p->wCOMPRESSOR_Atack       = wValue;    break;
    case GP4_Compressor_Ratio:       p->wCOMPRESSOR_Ratio       = wValue;    break;
    case GP4_Compressor_Threshold:   p->wCOMPRESSOR_Threshould  = wValue;    break;
    case GP4_Compressor_Gain:        p->wCOMPRESSOR_Gain        = wValue;    break;
    case GP4_Amp_Channel:            p->wActiveChanel           = wValue;    break;
    case GP4_Warp:                   p->wWARPZ                  = wValue;    break; // VERIFY!!
    case GP4_Amp_Warp:               p->wWARPY                  = wValue;    break; // VERIFY!!
    case GP4_Cabinet_Warp:           p->wWARPX                  = wValue;    break; // VERIFY!!
    case GP4_Channel_FS_Mode:        p->wAmpChannelFootSwitch   = wValue;    break;
    case GP4_Gate_Type:              
        if( par.m_dwValue == 768 /*silencer*/ )   p->wNOISEGATE_Type = 0;
        else if( par.m_dwValue == 769 /*pluck*/ ) p->wNOISEGATE_Type = 1;
        else ASSERT(0);
        break;
    case GP4_Gate_Enable:            p->wNOISEGATE_OnOff           = wValue; break;
    case GP4_Gate_Attack:            p->wNOISEGATE_Attack          = wValue; break;
    case GP4_Gate_Threshold:         p->wNOISEGATE_Threshould      = wValue; break;
    case GP4_Gate_Pluck_Sens:        p->wNOISEGATE_Pluck_Sensitivity = wValue; break;
    case GP4_Wham_IPS_Mod_Type:      Set_Wham_IPS_Mod_Type( par, p );        break;
    case GP4_Wham_IPS_Mod_Enable:    Set_Wham_IPS_Mod_Enable( par, p );      break;
    case GP4_Chorus_Level:           p->wCHORUSMOD_Level           = wValue; break;
    case GP4_Chorus_Speed:           p->wCHORUSMOD_Param1          = wValue; break;
    case GP4_Chorus_Depth:           p->wCHORUSMOD_Param2          = wValue; break;
    case GP4_Chorus_Predelay:        p->wCHORUSMOD_Param3          = wValue; break;
    case GP4_Chorus_Waveform:        p->wCHORUSMOD_Param4          = wValue; break;
    case GP4_Chorus_Balance:         p->wCHORUSMOD_Param5          = wValue; break;
    case GP4_Flanger_Mix:            p->wCHORUSMOD_Level           = wValue; break;
    case GP4_Flanger_Speed:          p->wCHORUSMOD_Param1          = wValue; break;
    case GP4_Flanger_Depth:          p->wCHORUSMOD_Param2          = wValue; break;
    case GP4_Flanger_Regen:          p->wCHORUSMOD_Param3          = wValue; break;
    case GP4_Flanger_Waveform:       p->wCHORUSMOD_Param4          = wValue; break;
    case GP4_Flanger_Balance:        p->wCHORUSMOD_Param5          = wValue; break;
    case GP4_Phaser_Speed:           p->wCHORUSMOD_Param1          = wValue; break;
    case GP4_Phaser_Depth:           p->wCHORUSMOD_Param2          = wValue; break;
    case GP4_Phaser_Level:           p->wCHORUSMOD_Level           = wValue; break;
    case GP4_Phaser_Regen:           p->wCHORUSMOD_Param3          = wValue; break;
    case GP4_Phaser_Waveform:        p->wCHORUSMOD_Param4          = wValue; break;
    case GP4_Phaser_Balance:         p->wCHORUSMOD_Param5          = wValue; break;
    case GP4_Trig_Flanger_LFO_Start: p->wCHORUSMOD_Param3          = wValue; break;
    case GP4_Trig_Flanger_Mix:       p->wCHORUSMOD_Level           = wValue; break; // ??? VERIFY!
    case GP4_Trig_Flanger_Speed:     p->wCHORUSMOD_Param1          = wValue; break;
    case GP4_Trig_Flanger_Sens:      p->wCHORUSMOD_Param2          = wValue; break;
    case GP4_Trig_Phaser_LFO_Start:  p->wCHORUSMOD_Param3          = wValue; break;
    case GP4_Trig_Phaser_Mix:        p->wCHORUSMOD_Level           = wValue; break; // ??? VERIFY!
    case GP4_Trig_Phaser_Speed:      p->wCHORUSMOD_Param1          = wValue; break;
    case GP4_Trig_Phaser_Sens:       p->wCHORUSMOD_Param2          = wValue; break;
    case GP4_Tremolo_Depth:          p->wCHORUSMOD_Param2          = wValue; break;
    case GP4_Tremolo_Speed:          p->wCHORUSMOD_Param1          = wValue; break;
    case GP4_Tremolo_Waveform:       p->wCHORUSMOD_Param3          = wValue; break;
    case GP4_Panner_Depth:           p->wCHORUSMOD_Param2          = wValue; break;
    case GP4_Panner_Speed:           p->wCHORUSMOD_Param1          = wValue; break;
    case GP4_Panner_Waveform:        p->wCHORUSMOD_Param3          = wValue; break;
    case GP4_Vibrato_Speed:          p->wCHORUSMOD_Param1          = wValue; break;
    case GP4_Vibrato_Depth:          p->wCHORUSMOD_Param2          = wValue; break;
    case GP4_Vibrato_Waveform:       p->wCHORUSMOD_Param3          = wValue; break;
    case GP4_Rotary_Speed:           p->wCHORUSMOD_Param1          = wValue; break;
    case GP4_Rotary_Depth:           p->wCHORUSMOD_Param2          = wValue; break;
    case GP4_Rotary_Mix:             p->wCHORUSMOD_Level           = wValue; break; // ??? VERIFY!
    case GP4_Rotary_Doppler:         p->wCHORUSMOD_Param3          = wValue; break;
    case GP4_Rotary_X_Over:          p->wCHORUSMOD_Param4          = wValue; break;
    case GP4_Rotary_Balance:         p->wCHORUSMOD_Param5          = wValue; break; // ??? VERIFY! - missed in documentation!
    case GP4_YaYa_Pedal:             p->wCHORUSMOD_Param1          = wValue; break;
    case GP4_YaYa_Range:             p->wCHORUSMOD_Param3          = wValue; break;
    case GP4_YaYa_Mix:               p->wCHORUSMOD_Level           = wValue; break; // ??? VERIFY!
    case GP4_YaYa_Depth:             p->wCHORUSMOD_Param2          = wValue; break;
    case GP4_YaYa_Balance:           p->wCHORUSMOD_Param4          = wValue; break;
    case GP4_AutoYa_Range:           p->wCHORUSMOD_Param3          = wValue; break;
    case GP4_AutoYa_Mix:             p->wCHORUSMOD_Level           = wValue; break; // ??? VERIFY!
    case GP4_AutoYa_Speed:           p->wCHORUSMOD_Param1          = wValue; break;
    case GP4_AutoYa_Depth:           p->wCHORUSMOD_Param2          = wValue; break;
    case GP4_AutoYa_Balance:         p->wCHORUSMOD_Param4          = wValue; break;
    case GP4_SynthTalk_Vox:          p->wCHORUSMOD_Param3          = wValue; break;
    case GP4_SynthTalk_Attack:       p->wCHORUSMOD_Param1          = wValue; break;
    case GP4_SynthTalk_Release:      p->wCHORUSMOD_Param2          = wValue; break;
    case GP4_SynthTalk_Sens:         p->wCHORUSMOD_Level           = wValue; break; // ??? VERIFY!
    case GP4_SynthTalk_Balance:      p->wCHORUSMOD_Param4          = wValue; break;
    case GP4_Envelope_Mix:           p->wCHORUSMOD_Level           = wValue; break; // ??? VERIFY!
    case GP4_Envelope_Range:         p->wCHORUSMOD_Param2          = wValue; break;
    case GP4_Envelope_Sens:          p->wCHORUSMOD_Param1          = wValue; break;
    case GP4_Envelope_Balance:       p->wCHORUSMOD_Param3          = wValue; break;
    case GP4_Detune_Level:           p->wCHORUSMOD_Level           = wValue; break; // ??? VERIFY!
    case GP4_Detune_Amount:          p->wCHORUSMOD_Param1          = wValue; break;
    case GP4_Detune_Balance:         p->wCHORUSMOD_Param2          = wValue; break;
    case GP4_Detune_Amount2:         p->wCHORUSMOD_Param1          = wValue; break; // ?????????????????????
    case GP4_Pitch_Shift_Amount:     p->wCHORUSMOD_Param1          = wValue; break;
    case GP4_Pitch_Shift_Level:      p->wCHORUSMOD_Level           = wValue; break;
    case GP4_Pitch_Shift_Amount2:    p->wCHORUSMOD_Param1          = wValue; break; // ?????????????????????
    case GP4_Pitch_Shift_Balance:    p->wCHORUSMOD_Param2          = wValue; break;
    case GP4_Whammy_Pedal:           p->wWHAMMY_Parameter2         = wValue; break;
    case GP4_Whammy_Mix:             p->wWHAMMY_IPS_PITCH_DETUNER_Level = wValue; break; // ??? VERIFY!
    case GP4_Whammy_Amount:          p->wWHAMMY_Parameter1         = wValue; break;
    case GP4_Delay_Type:             Set_Delay_Type( par, p );               break;
    case GP4_Delay_Enable:           p->wDELAY_OnOff               = wValue; break;
    case GP4_Delay_Level:            p->wDELAY_Level               = wValue; break;
    case GP4_Delay_Time:             p->wDELAY_Time                = wValue; break;
    case GP4_Delay_Feedback:         p->wDELAY_Feedback            = wValue; break;
    case GP4_Delay_Duck_Thresh:      p->wDELAY_Threshould          = wValue; break;
    case GP4_Delay_Duck_Atten:       p->wDELAY_Attenuation         = wValue; break;
    case GP4_Delay_Balance:          p->wDELAY_LRBalance           = wValue; break;
    case GP4_Delay_Spread:           p->wDELAY_Spread              = wValue; break;
    case GP4_Reverb_Type:            Set_Reverb_Type( par, p );              break;
    case GP4_Reverb_Enable:          p->wREVERB_OnOff              = wValue; break;
    case GP4_Reverb_Predelay:        p->wREVERB_Predelay           = wValue; break;
    case GP4_Reverb_Damping:         p->wREVERB_Dumping            = wValue; break;
    case GP4_Reverb_Level:           p->wREVERB_Level              = wValue; break;
    case GP4_Reverb_Decay:           p->wREVERB_Decay              = wValue; break;
    case GP4_Reverb_Balance:         p->wREVERB_LRBalance          = wValue; break;
    case GP4_Stompbox_Type:          Set_Stompbox_Type( par, p );            break;
    case GP4_Stompbox_Enable:        p->wSTOMPBOX_OnOff            = wValue; break;
    case GP4_Stompbox_Drive:         p->wSTOMPBOX_Gain             = wValue; break;
    case GP4_Stompbox_Tone:          p->wSTOMPBOX_Param1           = wValue; break;
    case GP4_Stompbox_Level:         p->wSTOMPBOX_Level            = wValue; break;
    case GP4_Stompbox_Distortion:    p->wSTOMPBOX_Gain             = wValue; break;
    case GP4_Stompbox_Filter:        p->wSTOMPBOX_Param1           = wValue; break;
    case GP4_Stompbox_Volume:        p->wSTOMPBOX_Level            = wValue; break;
    case GP4_Stompbox_Distortion2:   p->wSTOMPBOX_Gain             = wValue; break;
    case GP4_Stompbox_Tone2:         p->wSTOMPBOX_Param1           = wValue; break;
    case GP4_Stompbox_Level2:        p->wSTOMPBOX_Level            = wValue; break;
    case GP4_Stompbox_Gain:          p->wSTOMPBOX_Gain             = wValue; break;
    case GP4_Stompbox_Level3:        p->wSTOMPBOX_Level            = wValue; break;
    case GP4_Stompbox_Sustain:       p->wSTOMPBOX_Gain             = wValue; break;
    case GP4_Stompbox_Tone3:         p->wSTOMPBOX_Param1           = wValue; break;
    case GP4_Stompbox_Volume2:       p->wSTOMPBOX_Level            = wValue; break;
    case GP4_Stompbox_Drive2:        p->wSTOMPBOX_Gain             = wValue; break;
    case GP4_Stompbox_Level4:        p->wSTOMPBOX_Level            = wValue; break;
    case GP4_Stompbox_Gain2:         p->wSTOMPBOX_Gain             = wValue; break;
    case GP4_Stompbox_Tone4:         p->wSTOMPBOX_Param1           = wValue; break;
    case GP4_Stompbox_Clean:         p->wSTOMPBOX_Param2           = wValue; break;
    case GP4_Stompbox_Volume3:       p->wSTOMPBOX_Level            = wValue; break;
    case GP4_Stompbox_Gain3:         p->wSTOMPBOX_Gain             = wValue; break;
    case GP4_Stompbox_Butt:          p->wSTOMPBOX_Param1           = wValue; break;
    case GP4_Stompbox_Face:          p->wSTOMPBOX_Param2           = wValue; break;
    case GP4_Stompbox_Loud:          p->wSTOMPBOX_Level            = wValue; break;
    case GP4_Stompbox_Fuzz:          p->wSTOMPBOX_Gain             = wValue; break;
    case GP4_Stompbox_Volume4:       p->wSTOMPBOX_Level            = wValue; break;
    case GP4_Stompbox_Distortion3:   p->wSTOMPBOX_Gain             = wValue; break;
    case GP4_Stompbox_Mid_Freq:      p->wSTOMPBOX_Param1           = wValue; break;
    case GP4_Stompbox_Mid_Level:     p->wSTOMPBOX_Param2           = wValue; break;
    case GP4_Stompbox_Low:           p->wMZONE_LowFreq             = wValue; break;
    case GP4_Stompbox_High:          p->wMZONE_HiLevel             = wValue; break;
    case GP4_Stompbox_Level5:        p->wSTOMPBOX_Level            = wValue; break;
    case GP4_Chan_Amp_Type:          Set_Amp_Type( par, p );                 break; // seems no reason, because emulation data is stored in <genetx> section
    case GP4_Chan_Amp_Gain:          Set_Amp_Gain( par, p );                 break;
    case GP4_Chan_Amp_Level:         Set_Amp_Level( par, p );                break;
    case GP4_Chan_Bass_Freq:         Set_Bass_Freq( par, p );                break;
    case GP4_Chan_Bass_Level:        Set_Bass_Level( par, p );               break;
    case GP4_Chan_Mid_Freq:          Set_Mid_Freq( par, p );                 break;
    case GP4_Chan_Mid_Level:         Set_Mid_Level( par, p );                break;
    case GP4_Chan_Treble_Freq:       Set_Trebe_Freq( par, p );               break;
    case GP4_Chan_Treble_Level:      Set_Trebe_Level( par, p );              break;
    case GP4_Chan_EQ_Enable:         Set_EQ_Enable( par, p );                break;
    case GP4_Chan_Presence:          Set_Chan_Presence( par, p );            break;
    case GP4_Chan_Cab_Type:          Set_Cab_Type( par, p );                 break; // seems no reason, because emulation data is store in <genetx> section
    case GP4_Chan_Cab_Tuning:        Set_Cab_Tuning( par, p );               break;

    case GP4_Preset_Level:           p->wPresetLevel               = wValue; break;

    case GP4_IPS_Amount:             p->wWHAMMY_Parameter1         = wValue; break;
    case GP4_IPS_Scale:              p->wWHAMMY_Parameter2         = wValue; break;
    case GP4_IPS_Key:                p->wIPS_Key                   = wValue; break;
    case GP4_IPS_Level:              p->wWHAMMY_IPS_PITCH_DETUNER_Level = wValue; break;

    case GP4_Talker_Mic_Level:       p->wWHAMMY_IPS_PITCH_DETUNER_Level = wValue; break; // ! MAYBE should be wWHAMMY_Parameter1 - VERIFY !

    case GP4_Pedal_Assign_1:         Set_Pedal_Assign( par, p );             break;
    //case GP4_V_Switch_Pedal_Assign_1:

    case GP4_Pedal_Min_1:            Set_Pedal_MinMax( par, p );             break;
    //case GP4_Wah_Min:                

    case GP4_Pedal_Max_1:            Set_Pedal_MinMax( par, p );             break;
    //case GP4_Wah_Max:                

    case GP4_Pedal_Assign_2:         Set_Pedal_Assign( par, p );             break;
    case GP4_Pedal_Min_2:            Set_Pedal_MinMax( par, p );             break;
    case GP4_Pedal_Max_2:            Set_Pedal_MinMax( par, p );             break;
    case GP4_Pedal_Assign_3:         Set_Pedal_Assign( par, p );             break;
    case GP4_Pedal_Min_3:            Set_Pedal_MinMax( par, p );             break;
    case GP4_Pedal_Max_3:            Set_Pedal_MinMax( par, p );             break;

    case GP4_LFO1_Assign:            Set_LFO_Assign( par, p );               break;
    //case GP4_LFO2_Assign:            

    case GP4_LFO1_Min:               Set_LFO_MinMax( par, p );               break;
    //case GP4_LFO2_Min:               

    case GP4_LFO1_Max:               Set_LFO_MinMax( par, p );               break;
    //case GP4_LFO2_Max:               

    case GP4_LFO1_Speed:             Set_LFO_Speed( par, p );                break;
    //case GP4_LFO2_Speed:             

    case GP4_LFO1_Waveform:          Set_LFO_WaveForm( par, p );             break;
    //case GP4_LFO2_Waveform:          

    case GP4_V_Switch_Assign_1:      Set_Ctrl_Assign( par, p );              break;
    //case GP4_Control_A_Assign_1:     
    //case GP4_Control_B_Assign_1:     
    //case GP4_Control_C_Assign_1:     

    case GP4_V_Switch_Min:           Set_Ctrl_MinMax( par, p );              break;
    //case GP4_Control_A_Min_1:        
    //case GP4_Control_B_Min_1:        
    //case GP4_Control_C_Min_1:        

    case GP4_V_Switch_Max:           Set_Ctrl_MinMax( par, p );              break;
    //case GP4_Control_A_Max_1:        
    //case GP4_Control_B_Max_1:        
    //case GP4_Control_C_Max_1:        

    case GP4_Control_A_Assign_2:     Set_Ctrl_Assign( par, p );              break;
    //case GP4_Control_B_Assign_2:     
    //case GP4_Control_C_Assign_2:     

    case GP4_Control_A_Min_2:        Set_Ctrl_MinMax( par, p );              break;
    //case GP4_Control_B_Min_2:        
    //case GP4_Control_C_Min_2:        

    case GP4_Control_A_Max_2:        Set_Ctrl_MinMax( par, p );              break;
    //case GP4_Control_B_Max_2:        
    //case GP4_Control_C_Max_2:        

    case GP4_Control_A_Assign_3:     Set_Ctrl_Assign( par, p );              break;
    //case GP4_Control_B_Assign_3:     
    //case GP4_Control_C_Assign_3:     

    case GP4_Control_A_Min_3:        Set_Ctrl_MinMax( par, p );              break;
    //case GP4_Control_B_Min_3:        
    //case GP4_Control_C_Min_3:        

    case GP4_Control_A_Max_3:        Set_Ctrl_MinMax( par, p );              break;
    //case GP4_Control_B_Max_3:        
    //case GP4_Control_C_Max_3:        

    case GP4_V_Switch_Type:          Set_Ctrl_Type( par, p );                break;
    //case GP4_Control_A_Type:         
    //case GP4_Control_B_Type:         
    //case GP4_Control_C_Type:         

    case GP4_V_Switch_Enable:        p->wVSWITCH_State = wValue;             break;
        break;
    }
}

void CGP4File::Set_Wham_IPS_Mod_Type( const CGP4Param& par, LPPRESET p )
{
    WORD wSel = 0;
    if( par.m_csName == "Wham/IPS/Mod Type" )
    {
        switch( par.m_dwValue )
        {
        case 1344:  wSel = 0; break; // Whammy
        case 1345:  wSel = 1; break; // IPS
        case 1346:  wSel = 2; break; // Detune
        case 1347:  wSel = 3; break; // Pitch Shift
        // VERIFY: !!
        case 1348:  wSel = 4; break; // talker1
        case 1349:  wSel = 5; break; // talker2
        case 1350:  wSel = 6; break; // talker3
        case 1351:  wSel = 7; break; // talker4
        case 1352:  wSel = 8; break; // talker5
        default:
            ASSERT(0);
            break;
        }

        p->wWHAMMY_IPS_TALK_Selection = wSel;
    }
    else
    if( par.m_csName == "Mod Type" )
    {
        switch( par.m_dwValue )
        {
        case 857: wSel = 0;  break; // Chorus
        case 858: wSel = 1;  break; // Flanger
        case 859: wSel = 3;  break; // Triggered Flanger !!
        case 860: wSel = 2;  break; // Phaser            !!
        case 861: wSel = 4;  break; // Triggered Phaser
        case 862: wSel = 5;  break; // Tremolo
        case 863: wSel = 6;  break; // Panner
        case 864: wSel = 7;  break; // Vibrato
        case 865: wSel = 8;  break; // Rotary
        case 866: wSel = 9;  break; // Auto Ya
        case 867: wSel = 10; break; // Ya Ya
        case 868: wSel = 11; break; // Synthtalk
        case 869: wSel = 12; break; // Envelope
        case 870: wSel = 13; break; // Detune
        case 871: wSel = 14; break; // Pitch
        default:
            ASSERT(0);
            break;
        }

        p->wCHORUSMOD_Type = wSel;
    }
    else
        ASSERT(0);
}

void CGP4File::Set_Wham_IPS_Mod_Enable( const CGP4Param& par, LPPRESET p )
{
    if( par.m_csName == "Wham/IPS/Mod Enable" )
    {
        p->wWHAMMY_IPS_TALK_OnOff = (WORD)par.m_dwValue;
    }
    else
    if( par.m_csName == "Mod Enable" )
    {
        p->wCHORUSMOD_OnOff = (WORD)par.m_dwValue;
    }
    else
        ASSERT(0);
}

void CGP4File::Set_Delay_Type( const CGP4Param& par, LPPRESET p )
{
    WORD wValue = 0;

    switch( par.m_dwValue )
    {
    case 1027: wValue = 0; break; // Mono
    case 1028: wValue = 1; break; // Ping-Pong
    case 1029: wValue = 2; break; // Analog
    case 1030: wValue = 3; break; // Analog Pong
    case 1031: wValue = 4; break; // Spread
    default:
        ASSERT(0);
        break;
    }

    p->wDELAY_Type = wValue;
}

void CGP4File::Set_Reverb_Type( const CGP4Param& par, LPPRESET p )
{
    WORD wValue = 0;

    switch( par.m_dwValue )
    {
    case 1107: wValue = 0; break; // Studio
    case 1108: wValue = 1; break; // Room
    case 1109: wValue = 2; break; // Club
    case 1110: wValue = 3; break; // Plate
    case 1111: wValue = 4; break; // Hall
    case 1112: wValue = 5; break; // Amphitheater
    case 1113: wValue = 6; break; // Church
    case 1114: wValue = 7; break; // Garage
    case 1115: wValue = 8; break; // Arena
    case 1116: wValue = 9; break; // Spring
    default:
        ASSERT(0);
        break;
    }

    p->wREVERB_Type = wValue;
}

void CGP4File::Set_Stompbox_Type( const CGP4Param& par, LPPRESET p )
{
    WORD wValue = 0;

    switch( par.m_dwValue )
    {
    case 1280: wValue = 0; break; // Screamer
    case 1281: wValue = 1; break; // Rodent
    case 1282: wValue = 2; break; // DS Dist
    case 1283: wValue = 3; break; // DOD 250
    case 1284: wValue = 4; break; // Big MP
    case 1285: wValue = 5; break; // Guy OD
    case 1286: wValue = 6; break; // SparkDrive
    case 1287: wValue = 7; break; // Grunge
    case 1288: wValue = 8; break; // Fuzzy
    case 1289: wValue = 9; break; // Zone
    default:
        ASSERT(0);
        break;
    }

    p->wSTOMPBOX_Type = wValue;
}

void CGP4File::Set_Cab_Type( const CGP4Param& par, LPPRESET p )
{
    if( par.m_csName == "Chan. 1 Cab Type" )
    {
        p->wGreen_CAB_Type = (WORD)( par.m_dwValue - 537 ); // 537 - is the DIRECT cab constant in g4p file 
    }
    else
    if( par.m_csName == "Chan. 2 Cab Type" )
    {
        p->wRed_CAB_Type = (WORD)( par.m_dwValue - 537 );
    }
    else
    {
        ASSERT(0);
    }
}

void CGP4File::Set_Amp_Type( const CGP4Param& par, LPPRESET p )
{
    if( par.m_csName == "Chan. 1 Amp Type" )
    {
        p->wGreen_AMP_Type = (WORD)( par.m_dwValue - 269 ); // 269 - is the DIRECT amp constant in g4p file 
    }
    else
    if( par.m_csName == "Chan. 2 Amp Type" )
    {
        p->wRed_AMP_Type = (WORD)( par.m_dwValue - 269 );
    }
    else
    {
        ASSERT(0);
    }
}

void CGP4File::Set_Amp_Gain( const CGP4Param& par, LPPRESET p )
{
    if( par.m_csName == "Chan. 1 Amp Gain" )
    {
        p->wGreen_TONEAMP_Gain = (WORD)par.m_dwValue;
    }
    else
    if( par.m_csName == "Chan. 2 Amp Gain" )
    {
        p->wRed_TONEAMP_Gain = (WORD)par.m_dwValue;
    }
    else
    {
        ASSERT(0);
    }
}

void CGP4File::Set_Amp_Level( const CGP4Param& par, LPPRESET p )
{
    if( par.m_csName == "Chan. 1 Amp Level" )
    {
        p->wGreen_TONEAMP_Level = (WORD)par.m_dwValue;
    }
    else
    if( par.m_csName == "Chan. 2 Amp Level" )
    {
        p->wRed_TONEAMP_Level = (WORD)par.m_dwValue;
    }
    else
    {
        ASSERT(0);
    }
}

void CGP4File::Set_Bass_Freq( const CGP4Param& par, LPPRESET p )
{
    if( par.m_csName == "Chan. 1 Bass Freq." )
    {
        p->wGreen_BASS_Freq = (WORD)par.m_dwValue;
    }
    else
    if( par.m_csName == "Chan. 2 Bass Freq." )
    {
        p->wRed_BASS_Freq = (WORD)par.m_dwValue;
    }
    else
    {
        ASSERT(0);
    }
}

void CGP4File::Set_Bass_Level( const CGP4Param& par, LPPRESET p )
{
    if( par.m_csName == "Chan. 1 Bass Level" )
    {
        p->wGreen_TONE_BASS = (WORD)par.m_dwValue;
    }
    else
    if( par.m_csName == "Chan. 2 Bass Level" )
    {
        p->wRed_TONE_BASS = (WORD)par.m_dwValue;
    }
    else
    {
        ASSERT(0);
    }
}

void CGP4File::Set_Mid_Freq( const CGP4Param& par, LPPRESET p )
{
    if( par.m_csName == "Chan. 1 Mid Freq." )
    {
        p->wGreen_MID_Freq = (WORD)par.m_dwValue;
    }
    else
    if( par.m_csName == "Chan. 2 Mid Freq." )
    {
        p->wRed_MID_Freq = (WORD)par.m_dwValue;
    }
    else
    {
        ASSERT(0);
    }
}

void CGP4File::Set_Mid_Level( const CGP4Param& par, LPPRESET p )
{
    if( par.m_csName == "Chan. 1 Mid Level" )
    {
        p->wGreen_TONE_Midrange = (WORD)par.m_dwValue;
    }
    else
    if( par.m_csName == "Chan. 2 Mid Level" )
    {
        p->wRed_TONE_Midrange = (WORD)par.m_dwValue;
    }
    else
    {
        ASSERT(0);
    }
}

void CGP4File::Set_Trebe_Freq( const CGP4Param& par, LPPRESET p )
{
    if( par.m_csName == "Chan. 1 Treble Freq." )
    {
        p->wGreen_TREB_Freq = (WORD)par.m_dwValue;
    }
    else
    if( par.m_csName == "Chan. 2 Treble Freq." )
    {
        p->wRed_TREB_Freq = (WORD)par.m_dwValue;
    }
    else
    {
        ASSERT(0);
    }
}

void CGP4File::Set_Trebe_Level( const CGP4Param& par, LPPRESET p )
{
    if( par.m_csName == "Chan. 1 Treble Level" )
    {
        p->wGreen_TONE_TREBE = (WORD)par.m_dwValue;
    }
    else
    if( par.m_csName == "Chan. 2 Treble Level" )
    {
        p->wRed_TONE_TREBE = (WORD)par.m_dwValue;
    }
    else
    {
        ASSERT(0);
    }
}

void CGP4File::Set_EQ_Enable( const CGP4Param& par, LPPRESET p )
{
    if( par.m_csName == "Chan. 1 EQ Enable" )
    {
        p->wGreen_EQUALIZER_OnOff = (WORD)par.m_dwValue;
    }
    else
    if( par.m_csName == "Chan. 2 EQ Enable" )
    {
        p->wRed_EQUALIZER_OnOff = (WORD)par.m_dwValue;
    }
    else
    {
        ASSERT(0);
    }
}

void CGP4File::Set_Chan_Presence( const CGP4Param& par, LPPRESET p )
{
    if( par.m_csName == "Chan. 1 Presence" )
    {
        p->wGreen_Presence = (WORD)par.m_dwValue;
    }
    else
    if( par.m_csName == "Chan. 2 Presence" )
    {
        p->wRed_Presence = (WORD)par.m_dwValue;
    }
    else
    {
        ASSERT(0);
    }
}

void CGP4File::Set_Cab_Tuning( const CGP4Param& par, LPPRESET p )
{
    if( par.m_csName == "Chan. 1 Cab Tuning" )
    {
        p->wGreen_CABTUNE = (WORD)par.m_dwValue;
    }
    else
    if( par.m_csName == "Chan. 2 Cab Tuning" )
    {
        p->wRed_CABTUNE = (WORD)par.m_dwValue;
    }
    else
    {
        ASSERT(0);
    }
}

void CGP4File::Set_Pedal_Assign( const CGP4Param& par, LPPRESET p )
{
    if( par.m_csName == "Pedal Assign 1" )
    {
        p->wEXPPdl1Assign = (WORD)par.m_dwValue;
    }
    else
    if( par.m_csName == "Pedal Assign 2" )
    {
        p->wEXPPdl2Assign = (WORD)par.m_dwValue;
    }
    else
    if( par.m_csName == "Pedal Assign 3" )
    {
        p->wEXPPdl3Assign = (WORD)par.m_dwValue;
    }
    else
    if( par.m_csName == "V-Switch Pedal Assign 1" )
    {
        //p->dwEXPPdl1Assign = par.m_dwValue; // NOT TRUE, investigate!
    }
    else
    {
        ASSERT(0);
    }
}

void CGP4File::Set_Pedal_MinMax( const CGP4Param& par, LPPRESET p )
{
    if( par.m_csName == "Pedal Min 1" )
    {
        p->wEXPPdl1Min = (WORD)par.m_dwValue;
    }
    else
    if( par.m_csName == "Pedal Max 1" )
    {
        p->wEXPPdl1Max = (WORD)par.m_dwValue;
    }
    else
    if( par.m_csName == "Pedal Min 2" )
    {
        p->wEXPPdl2Min = (WORD)par.m_dwValue;
    }
    else
    if( par.m_csName == "Pedal Max 2" )
    {
        p->wEXPPdl2Max = (WORD)par.m_dwValue;
    }
    else
    if( par.m_csName == "Pedal Min 3" )
    {
        p->wEXPPdl3Min = (WORD)par.m_dwValue;
    }
    else
    if( par.m_csName == "Pedal Max 3" )
    {
        p->wEXPPdl3Max = (WORD)par.m_dwValue;
    }
    else                                                                               // | never seen in xml, but let it be so, in case they fix this in the future
    if( par.m_dwID == GP4_Wah_Max && ( par.m_csName == "Wah Min" || par.m_csName == "Wah Max" ) ) // xml contains typo - 'Wah Min', when it is actually 'Wah Max' :)
    {
        p->wWAH_Pdl1_Max = (WORD)par.m_dwValue;
    }
    else
    if( par.m_csName == "Wah Min" )
    {
        p->wWAH_Pdl1_Min = (WORD)par.m_dwValue;
    }
    else
    {
        ASSERT(0);
    }
}

void CGP4File::Set_LFO_Assign( const CGP4Param& par, LPPRESET p )
{
    if( par.m_csName == "LFO1 Assign" )
    {
        p->wLFO1Assign = (WORD)par.m_dwValue;
    }
    else
    if( par.m_csName == "LFO2 Assign" )
    {
        p->wLFO2Assign = (WORD)par.m_dwValue;
    }
    else
    {
        ASSERT(0);
    }
}

void CGP4File::Set_LFO_MinMax( const CGP4Param& par, LPPRESET p )
{
    if( par.m_csName == "LFO1 Min" )
    {
        p->wLFO1Min = (WORD)par.m_dwValue;
    }
    else
    if( par.m_csName == "LFO1 Max" )
    {
        p->wLFO1Max = (WORD)par.m_dwValue;
    }
    else
    if( par.m_csName == "LFO2 Min" )
    {
        p->wLFO2Min = (WORD)par.m_dwValue;
    }
    else
    if( par.m_csName == "LFO2 Max" )
    {
        p->wLFO2Max = (WORD)par.m_dwValue;
    }
    else
    {
        ASSERT(0);
    }
}

void CGP4File::Set_LFO_Speed( const CGP4Param& par, LPPRESET p )
{
    if( par.m_csName == "LFO1 Speed" )
    {
        p->wLFO1Speed = (WORD)par.m_dwValue;
    }
    else
    if( par.m_csName == "LFO2 Speed" )
    {
        p->wLFO2Speed = (WORD)par.m_dwValue;
    }
    else
    {
        ASSERT(0);
    }
}

void CGP4File::Set_LFO_WaveForm( const CGP4Param& par, LPPRESET p )
{
    if( par.m_csName == "LFO1 Waveform" )
    {
        p->wLFO1WaveForm = (WORD)par.m_dwValue;
    }
    else
    if( par.m_csName == "LFO2 Waveform" )
    {
        p->wLFO2WaveForm = (WORD)par.m_dwValue;
    }
    else
    {
        ASSERT(0);
    }
}

void CGP4File::Set_Ctrl_Assign( const CGP4Param& par, LPPRESET p )
{
    if( par.m_csName == "V-Switch Assign 1" )
    {
        // p->dwEXPPdl1Assign = par.m_dwValue; // NOT TRUE, investigate
    }
    else
    if( par.m_csName == "Control A Assign 1" )
    {
        p->wCTRLA_Lnk1_Assign = (WORD)par.m_dwValue;
    }
    else
    if( par.m_csName == "Control B Assign 1" )
    {
        p->wCTRLB_Lnk1_Assign = (WORD)par.m_dwValue;
    }
    else
    if( par.m_csName == "Control C Assign 1" )
    {
        p->wCTRLC_Lnk1_Assign = (WORD)par.m_dwValue;
    }
    else
    if( par.m_csName == "Control A Assign 2" )
    {
        p->wCTRLA_Lnk2_Assign = (WORD)par.m_dwValue;
    }
    else
    if( par.m_csName == "Control B Assign 2" )
    {
        p->wCTRLB_Lnk2_Assign = (WORD)par.m_dwValue;
    }
    else
    if( par.m_csName == "Control C Assign 2" )
    {
        p->wCTRLC_Lnk2_Assign = (WORD)par.m_dwValue;
    }
    else
    if( par.m_csName == "Control A Assign 3" )
    {
        p->wCTRLA_Lnk3_Assign = (WORD)par.m_dwValue;
    }
    else
    if( par.m_csName == "Control B Assign 3" )
    {
        p->wCTRLB_Lnk3_Assign = (WORD)par.m_dwValue;
    }
    else
    if( par.m_csName == "Control C Assign 3" )
    {
        p->wCTRLC_Lnk3_Assign = (WORD)par.m_dwValue;
    }
    else
    {
        ASSERT(0);
    }
}

void CGP4File::Set_Ctrl_MinMax( const CGP4Param& par, LPPRESET p )
{
    if( par.m_csName == "V-Switch Min" )
    {
        //p->wWAH_Pedal_Min = (WORD)par.m_dwValue; // 
    }
    else
    if( par.m_csName == "V-Switch Max" )
    {
        //p->wWAH_Pedal_Max = (WORD)par.m_dwValue; // NOT TRUE, investigate
    }
    else
    if( par.m_csName == "V-Switch Min 1" )
    {
        //p->wWAH_Pedal_Min = (WORD)par.m_dwValue; // 
    }
    else
    if( par.m_csName == "V-Switch Max 1" )
    {
        //p->wWAH_Pedal_Max = (WORD)par.m_dwValue; // 
    }
    else
    if( par.m_csName == "Control A Min 1" )
    {
        p->wCTRLA_Lnk1_Min = (WORD)par.m_dwValue;
    }
    else
    if( par.m_csName == "Control B Min 1" )
    {
        p->wCTRLB_Lnk1_Min = (WORD)par.m_dwValue;
    }
    else
    if( par.m_csName == "Control C Min 1" )
    {
        p->wCTRLC_Lnk1_Min = (WORD)par.m_dwValue;
    }
    else
    if( par.m_csName == "Control A Max 1" )
    {
        p->wCTRLA_Lnk1_Max = (WORD)par.m_dwValue;
    }
    else
    if( par.m_csName == "Control B Max 1" )
    {
        p->wCTRLB_Lnk1_Max = (WORD)par.m_dwValue;
    }
    else
    if( par.m_csName == "Control C Max 1" )
    {
        p->wCTRLC_Lnk1_Max = (WORD)par.m_dwValue;
    }
    else
    if( par.m_csName == "Control A Min 2" )
    {
        p->wCTRLA_Lnk2_Min = (WORD)par.m_dwValue;
    }
    else
    if( par.m_csName == "Control B Min 2" )
    {
        p->wCTRLB_Lnk2_Min = (WORD)par.m_dwValue;
    }
    else
    if( par.m_csName == "Control C Min 2" )
    {
        p->wCTRLC_Lnk2_Min = (WORD)par.m_dwValue;
    }
    else
    if( par.m_csName == "Control A Max 2" )
    {
        p->wCTRLA_Lnk2_Max = (WORD)par.m_dwValue;
    }
    else
    if( par.m_csName == "Control B Max 2" )
    {
        p->wCTRLB_Lnk2_Max = (WORD)par.m_dwValue;
    }
    else
    if( par.m_csName == "Control C Max 2" )
    {
        p->wCTRLC_Lnk2_Max = (WORD)par.m_dwValue;
    }
    else
    if( par.m_csName == "Control A Min 3" )
    {
        p->wCTRLA_Lnk3_Min = (WORD)par.m_dwValue;
    }
    else
    if( par.m_csName == "Control B Min 3" )
    {
        p->wCTRLB_Lnk3_Min = (WORD)par.m_dwValue;
    }
    else
    if( par.m_csName == "Control C Min 3" )
    {
        p->wCTRLC_Lnk3_Min = (WORD)par.m_dwValue;
    }
    else
    if( par.m_csName == "Control A Max 3" )
    {
        p->wCTRLA_Lnk3_Max = (WORD)par.m_dwValue;
    }
    else
    if( par.m_csName == "Control B Max 3" )
    {
        p->wCTRLB_Lnk3_Max = (WORD)par.m_dwValue;
    }
    else
    if( par.m_csName == "Control C Max 3" )
    {
        p->wCTRLC_Lnk3_Max = (WORD)par.m_dwValue;
    }
    else
    {
        ASSERT(0);
    }
}

void CGP4File::Set_Ctrl_Type( const CGP4Param& par, LPPRESET p )
{
    if( par.m_csName == "V-Switch Type" )
    {
        p->wWAH_Type = (WORD)par.m_dwValue; // VERIFY!
    }
    else
    if( par.m_csName == "Control A Type" )
    {
        p->wCTRLA_Type = (WORD)par.m_dwValue;
    }
    else
    if( par.m_csName == "Control B Type" )
    {
        p->wCTRLB_Type = (WORD)par.m_dwValue;
    }
    else
    if( par.m_csName == "Control C Type" )
    {
        p->wCTRLC_Type = (WORD)par.m_dwValue;
    }
    else
    {
        ASSERT(0);
    }
}